/**************************************************************************
*   Copyright (C) 2009 Chernov Dmitry                                     *
*   diman4ik.chernov@gmail.com                                            *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
*   This program is distributed in the hope that it will be useful,       *
*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
*   GNU General Public License for more details.                          *
*                                                                         *
*   You should have received a copy of the GNU General Public License     *
*   along with this program; if not, write to the                         *
*   Free Software Foundation, Inc.,                                       *
*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
***************************************************************************/

#ifndef KORNERS_GAME_ENGINE_AI
#define KORNERS_GAME_ENGINE_AI

#include "korners_game_engine.h"

#include <list>
#include <map>


class KornersGameEngineAI : public KornersEngine
{
    Q_OBJECT
public:
    explicit KornersGameEngineAI();
    void makeNextMove( const QPoint& src, const QPoint& dst );

    bool canSelect( const QPoint& src ) const;
    bool canMove( QPoint src, QPoint dst ) const;

    struct MoveInfo
    {
        QPoint begin;
        QPoint end;
        int profit;

        MoveInfo( QPoint pnt1, QPoint pnt2 )
        {
            begin = pnt1;
            end = pnt2;
        }
                
        MoveInfo( QPoint pnt1, QPoint pnt2, int p )
        {
            begin = pnt1;
            end = pnt2;
            profit = p;
        }

        bool operator < ( const MoveInfo& mvInf ) const
        {
            return profit < mvInf.profit; 
        }

        bool operator > ( const MoveInfo& mvInf ) const
        {
            return profit > mvInf.profit; 
        }
    };

private:
    /**
     *  Values for all cells
     */
    static const char static_values[];
    /**
     *  Value of current position
     */
    int m_curPositionVal;
    /**
     *  If the game is close to the end
     */
    bool m_endSpiel;
    /**
     *  Find the best move for computer
     */
    MoveInfo search( int depth );
    bool innerSearch( int* diff, int depth, owner tp );
    /**
     *  Positions sctatic value
     */
    inline int calcPosStaticValue( owner tp ) const
    {
        int summ = 0;

        for( int i = 0; i < FIELD_SIZE*FIELD_SIZE; i++ )
        {   
            if( m_field[i] == tp )
            {
                if( tp == opponent)
                    summ += static_values[i];
                if( tp == player )
		    summ += static_values[ rotateIndex(i) ];
            }
        }

        return summ;
    }
    /**
     *  Calc all the caps at the enemy base
     */
    inline int calcEnemyBaseCaps( owner tp )
    {
        int how_much = 0;
        for( int i = 0; i < N_CAPS/2; i++ )
        {
            if( tp == opponent )
            {
                if( m_field[ i%IN_ROW + IN_ROW + ( i/IN_ROW + FIELD_SIZE - N_ROWS )*FIELD_SIZE ] == opponent )
                    how_much++;
            }
            else if( tp == player )
            {
                if( m_field[ i%IN_ROW + ( i/IN_ROW )*FIELD_SIZE ] == player )
                    how_much++;
            }
        }

        return how_much;
    }

    /**
     *  Mid value of all computer caps coordinates
     */
    inline QPoint calcMidPosition()
    {
        QPoint mid( 0, 0 );

        for( int i = 0; i < FIELD_SIZE*FIELD_SIZE; i++ )
	{
	    if( m_field[i] == opponent )
		mid += indexToPoint(i);
	}

        mid /= N_CAPS/2;

        return mid;
    }
    /**
     *  Penalty for backward caps
     */
    inline int backwardPenalty( QPoint cap, QPoint mid )
    {
	if(  ( cap.x() > mid.x() ) && ( cap.y() > mid.y() ) )
	    return 0;

        QPoint diff = cap - mid;

        int deviation = diff.manhattanLength();
        if( deviation < 6 )
	    return 0;

        return deviation*deviation*deviation*deviation;
    }
    /**
     *  Bonus for cap grouping
     */
    inline int groupingBonus()
    {
        int bonus = 0;
        QPoint mid = calcMidPosition();

        for( int i = 0; i < FIELD_SIZE*FIELD_SIZE; i++ )
	{
	    if( m_field[i] == opponent )
		bonus -= backwardPenalty( indexToPoint(i), mid );
	}

        return bonus;
    }

    void correctValue( QPoint src, QPoint dst, int& value );

    inline int moveValue( QPoint src, QPoint dst )
    {
	if( !m_endSpiel )
	{
	    int ret = 0;//dst.x() - src.x() + dst.y() - src.y();
	    ret +=  (static_values[ pointToIndex(dst) ] -
		    static_values[ pointToIndex(src) ])*10;

	    if( ( src.x() > FIELD_SIZE - IN_ROW ) &&
		( src.y() > FIELD_SIZE - N_ROWS ) )
		ret -= 18;
	    
	    if( ( src.x() < IN_ROW ) &&
		( src.y() < N_ROWS ) )
		ret += 16;
	    return ret;
	}
        else
	    return (static_values[ pointToIndex(dst) ] -
		    static_values[ pointToIndex(src) ])*10;
    }

    /**
     * Collect all moves available from current position
     */
    void pointsToGo( QPoint from_where, QVector<int>& ret ) const;

    /**
     *  Compute desireability for available moves
     */
    int scoreAvailableMoves( std::list<MoveInfo>& moves, owner tp ); 
    int getScores( std::multimap<int, int>& all, std::list<MoveInfo>& ret, owner tp );
};

#endif
